package com.autumn.oauth2.zero.configure;

import java.util.Arrays;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import com.autumn.oauth2.zero.configuration.BeanConsts;
import com.autumn.oauth2.zero.provider.token.IdentityClaimsTokenEnhancer;
import com.autumn.security.core.authentication.UsernamePasswordAuthenticationProvider;
import com.autumn.security.core.userdetails.IAutumnUserDetailsService;
import com.autumn.security.crypto.IAutumnPasswordProvider;
import com.autumn.spring.boot.properties.AutumnAuthProperties;

/**
 * 服务器配置适配器
 * 
 * @author 老码农 2018-04-14 21:42:25
 */
@EnableConfigurationProperties({ AutumnAuthProperties.class })
public class ZoreAuthorizationServerConfigurerAdapter extends AuthorizationServerConfigurerAdapter {

	@Autowired
	private RedisConnectionFactory connectionFactory;

	@Autowired
	private IAutumnUserDetailsService autumnUserDetailsService;

	@Autowired
	private IAutumnPasswordProvider autumnPasswordProvider;

	@Autowired
	private AutumnAuthProperties autumnAuthConfigurationProperties;

	/**
	 * 
	 * @return
	 */
	@Primary
	@Bean(BeanConsts.AUTUMN_TOKEN_SERVICES)
	public DefaultTokenServices autumnTokenServices() {
		Integer expire = autumnAuthConfigurationProperties.getExpire();
		if (expire == null || expire.intValue() <= 0) {
			expire = -1; // 永不过期
		}
		DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
		defaultTokenServices.setAccessTokenValiditySeconds(expire);
		defaultTokenServices.setRefreshTokenValiditySeconds(expire);
		defaultTokenServices.setSupportRefreshToken(true);
		defaultTokenServices.setReuseRefreshToken(false);
		defaultTokenServices.setTokenStore(tokenStore());
		defaultTokenServices.setClientDetailsService(clientDetails());
		return defaultTokenServices;
	}

	@Bean
	public RedisTokenStore tokenStore() {
		return new RedisTokenStore(connectionFactory);
	}

	@Autowired
	private DataSource dataSource;

	@Autowired
	private AuthenticationManager authenticationManager;

	@Bean
	public ClientDetailsService clientDetails() {
		return new JdbcClientDetailsService(dataSource);
	}

	@Bean
	@Autowired
	public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore,
			ClientDetailsService clientDetailsService) {
		TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
		handler.setTokenStore(tokenStore);
		handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
		handler.setClientDetailsService(clientDetailsService);
		return handler;
	}

	@Bean
	@Primary
	public UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProvider() {
		return new UsernamePasswordAuthenticationProvider(this.autumnUserDetailsService, this.autumnPasswordProvider);
	}

	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
		TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
		tokenEnhancerChain.setTokenEnhancers(Arrays.asList(identityClaimsTokenEnhancer()));
		endpoints.authenticationManager(this.authenticationManager).userDetailsService(this.autumnUserDetailsService)
				.tokenStore(tokenStore()).tokenServices(autumnTokenServices()).tokenEnhancer(tokenEnhancerChain)
				.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
	}

	@Bean
	public IdentityClaimsTokenEnhancer identityClaimsTokenEnhancer() {
		return new IdentityClaimsTokenEnhancer();
	}

	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
		security.checkTokenAccess("permitAll()");
		security.allowFormAuthenticationForClients();

		// security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()")
		// .allowFormAuthenticationForClients();
	}

	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		clients.withClientDetails(clientDetails());
	}
}
